/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

package com.huawei.housekeeper.service.impl;

import com.huawei.housekeeper.common.enums.ErrorCode;
import com.huawei.housekeeper.commonutils.constants.DeletedFlagEnum;
import com.huawei.housekeeper.commonutils.constants.RabbitMqTopicConfig;
import com.huawei.housekeeper.commonutils.constants.TaskOrderAction;
import com.huawei.housekeeper.commonutils.entity.MessageServiceEntity;
import com.huawei.housekeeper.commonutils.entity.TaskMessageEntity;
import com.huawei.housekeeper.commonutils.exception.Assert;
import com.huawei.housekeeper.commonutils.result.ListRes;
import com.huawei.housekeeper.commonutils.utils.MessageServiceUtil;
import com.huawei.housekeeper.commonutils.utils.TokenUtil;
import com.huawei.housekeeper.controller.converter.TaskConverter;
import com.huawei.housekeeper.controller.request.DoTaskDto;
import com.huawei.housekeeper.controller.request.MyTaskDetailDto;
import com.huawei.housekeeper.controller.request.PageQueryTaskDto;
import com.huawei.housekeeper.controller.request.TaskDetailDto;
import com.huawei.housekeeper.controller.response.MyTaskDetailVo;
import com.huawei.housekeeper.controller.response.MyTaskListVo;
import com.huawei.housekeeper.controller.response.TaskDetailVo;
import com.huawei.housekeeper.controller.response.TaskPageListVo;
import com.huawei.housekeeper.dao.entity.Task;
import com.huawei.housekeeper.dao.mapper.TaskMapper;
import com.huawei.housekeeper.service.BaseService;
import com.huawei.housekeeper.service.ITaskService;
import com.huawei.saashousekeeper.config.TenantContext;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.collect.Lists;

import lombok.extern.log4j.Log4j2;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.RejectedExecutionException;

/**
 * 任务表服务类
 *
 * @author jwx1116205
 * @since 2022-03-02
 */
@Log4j2
@Service
public class TaskServiceImpl extends BaseService implements ITaskService {
    @Autowired
    private TaskMapper taskMapper;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    private MessageServiceUtil messageServiceUtil;

    @Autowired
    private TokenUtil tokenUtil;

    /**
     * 查询个人已接任务详情，除了用户名称和电话
     *
     * @param myTaskDetailDto 任务Dto
     * @return 任务详情
     */
    @Override
    public MyTaskDetailVo myTaskDetailDto(MyTaskDetailDto myTaskDetailDto) {
        LambdaQueryWrapper<Task> selectQuery = Wrappers.lambdaQuery();
        selectQuery.eq(Task::getId, myTaskDetailDto.getId());
        selectQuery.eq(Task::getDeleteFlag, DeletedFlagEnum.SERVICE_FLAGE_UNDELETED.getCode());
        Task taskOne = Optional.ofNullable(taskMapper.selectOne(selectQuery)).orElseGet(() -> {
            return new Task();
        });
        return TaskConverter.INSTANCE.toMyTaskDetailVo(taskOne);
    }

    /**
     * 查询个人已接任务
     *
     * @return 个人已接任务列表
     */
    @Override
    public List<MyTaskListVo> myTaskListDto() {
        LambdaQueryWrapper<Task> selectQuery = Wrappers.lambdaQuery();
        selectQuery.eq(Task::getDeleteFlag, DeletedFlagEnum.SERVICE_FLAGE_UNDELETED.getCode());
        selectQuery.eq(Task::getEmployeeId, tokenUtil.getUidFromHeader());
        selectQuery.eq(Task::getTaskStatus, TaskOrderAction.TASK_ORDER_ACCEPT.getAction());
        selectQuery.orderByDesc(Task::getAppointmentTime);
        List<Task> taskList = Optional.ofNullable(taskMapper.selectList(selectQuery)).orElseGet(() -> {
            return Lists.newArrayList();
        });
        return TaskConverter.INSTANCE.toMyTaskDetailListVo(taskList);
    }

    /**
     * 抢单或完成任务
     *
     * @param doTaskDto
     * @return id
     */
    @Override
    public Long updateTask(DoTaskDto doTaskDto) {
        Optional<TaskOrderAction> orderAction = TaskOrderAction.ofAction(doTaskDto.getAction());
        Task task = checkTaskStatusFromDoTask(doTaskDto.getId(), orderAction.get());

        // 人工抢单
        if (orderAction.get() == TaskOrderAction.TASK_ORDER_ACCEPT) {
            Assert.notEmpty(task.getVersion(), ErrorCode.VERSION_NOT_EMPTY.getCode(),
                ErrorCode.VERSION_NOT_EMPTY.getMessage());
            grapTaskOrder(task, TaskOrderAction.TASK_ORDER_ACCEPT);
        } else { // 取消或者完成任务
            taskMapper.updateById(task);
            sentMsgGrapOrderResult(BaseService.UPDATE_ONE_RECORD_SUCCESS, task, orderAction.get(), TenantContext.get(),
                tokenUtil.getUidFromHeader());
        }
        return task.getId();
    }

    /**
     * 抢单或完成任务时验证当前的任务状态,并设置当前任务的状态
     *
     * @param taskId
     * @param orderAction 操作Action
     * @return {@link Task}
     */
    private Task checkTaskStatusFromDoTask(Long taskId, TaskOrderAction orderAction) {
        // 查询任务信息
        LambdaQueryWrapper<Task> selectQuery = Wrappers.lambdaQuery();
        selectQuery.eq(Task::getId, taskId);
        selectQuery.eq(Task::getDeleteFlag, DeletedFlagEnum.SERVICE_FLAGE_UNDELETED.getCode());
        Task taskFromDb = taskMapper.selectOne(selectQuery);
        TaskOrderAction taskStatusAction = TaskOrderAction.ofAction(taskFromDb.getTaskStatus()).get();

        // 验证任务是否存在
        Assert.notNull(taskFromDb, ErrorCode.TASK_NOT_EXIST.getCode(), ErrorCode.TASK_NOT_EXIST.getMessage());
        switch (orderAction) {
            case TASK_ORDER_CANCEL: // 取消操作，任务的状态必须是已接单
                Assert.isTrue(taskStatusAction == TaskOrderAction.TASK_ORDER_ACCEPT,
                    ErrorCode.TASK_UPDATE_NOT_PERMISSION_CANCEL.getCode(),
                    ErrorCode.TASK_UPDATE_NOT_PERMISSION_CANCEL.getMessage());
                break;
            case TASK_ORDER_FINISHED: // 任务完成操作，任务的状态必须是已接单
                Assert.isTrue(taskStatusAction == TaskOrderAction.TASK_ORDER_ACCEPT,
                    ErrorCode.TASK_UPDATE_NOT_PERMISSION_FINISHED.getCode(),
                    ErrorCode.TASK_UPDATE_NOT_PERMISSION_FINISHED.getMessage());
                break;
            case TASK_ORDER_ACCEPT: // 抢单操作，任务的状态必须是待抢单
                Assert.isTrue(taskStatusAction == TaskOrderAction.TASK_ORDER_WAITING,
                    ErrorCode.TASK_UPDATE_NOT_PERMISSION_GRAP.getCode(),
                    ErrorCode.TASK_UPDATE_NOT_PERMISSION_GRAP.getMessage());
                break;
            default: // 初始化任务的状态操作，默认非法操作
                Assert.isTrue(taskStatusAction == null, ErrorCode.TASK_UPDATE_NOT_PERMISSION.getCode(),
                    ErrorCode.TASK_UPDATE_NOT_PERMISSION.getMessage());
                break;
        }
        taskFromDb.setTaskStatus(orderAction.getAction());
        taskFromDb.setEmployeeId(tokenUtil.getUidFromHeader());
        taskFromDb.setEmployeeName(tokenUtil.getUserNameFromHeader());
        return taskFromDb;
    }

    /**
     * 多线程抢单任务，使用乐观锁
     *
     * @param doTaskDto
     * @param orderAction
     */
    private void grapTaskOrder(Task task, TaskOrderAction orderAction) {
        try {
            String tenantDomain = TenantContext.get();
            String userId = tokenUtil.getUidFromHeader();
            threadPoolTaskExecutor.execute(() -> {
                int result = runWithTenantContext(tenantDomain, task, taskParam -> {
                    return taskMapper.grapTaskOrder(taskParam);
                });
                sentMsgGrapOrderResult(result, task, orderAction, tenantDomain, userId);
            });
        } catch (RejectedExecutionException ex) {
            log.error("-->worker grap task RejectedExecutionException：", ex);
        }
    }

    /**
     * 发送消息给订单中心和消息中心
     *
     * @param result 抢单结果
     * @param doTaskDto 任务id 更新前的乐观锁
     * @param orderAction 工人操作行为 accept/finished/cancel
     */
    private void sentMsgGrapOrderResult(int result, Task taskOne, TaskOrderAction orderAction, String tenantDomain,
        String userId) {
        try {
            MessageServiceEntity messageServiceEntity = new MessageServiceEntity();
            String msgTempage = "您好,【" + taskOne.getServiceName() + "】服务";
            switch (orderAction) {
                case TASK_ORDER_FINISHED:
                    msgTempage += ", 任务已完成!";
                    messageServiceEntity.setTitle("任务已完成：" + taskOne.getServiceName());
                    break;
                case TASK_ORDER_CANCEL:
                    msgTempage += ", 任务已取消!";
                    messageServiceEntity.setTitle("任务已取消：" + taskOne.getServiceName());
                    break;
                default:
                    if (result == BaseService.UPDATE_ONE_RECORD_SUCCESS) {
                        msgTempage += ", 抢单成功!";
                        messageServiceEntity.setTitle("抢单成功：" + taskOne.getServiceName());
                    } else {
                        msgTempage += ", 抢单失败!";
                        messageServiceEntity.setTitle("抢单失败：" + taskOne.getServiceName());
                    }
            }
            Date dateNow = new Date();

            // 异步消息给订单中心 action:accept/finished/cancel
            TaskMessageEntity taskMessageEntity = new TaskMessageEntity();
            taskMessageEntity.setTime(dateNow);
            taskMessageEntity.setAction(orderAction.getAction());
            taskMessageEntity.setOrderId(taskOne.getOrderId());
            taskMessageEntity.setEmployeeId(taskOne.getEmployeeId());
            messageServiceUtil.sendMessageByAmqTopic(RabbitMqTopicConfig.ORDER_TASK_DO_TASK, taskMessageEntity,
                tenantDomain);
            log.info("task tenantDomain : " + tenantDomain);

            // 异步消息给消息中心，发送给工人消息
            messageServiceEntity.setMsg(msgTempage);
            messageServiceEntity.setUserId(userId);
            messageServiceEntity.setTime(dateNow);
            messageServiceUtil.sendMessageByAmqTopic(RabbitMqTopicConfig.MESSAGE_TASK_ORDER, messageServiceEntity,
                tenantDomain);
        } catch (JsonProcessingException ex) {
            log.error("--->Capture exception info: ", ex);
        }
    }

    /**
     * 任务详情,除了用户名称和电话
     *
     * @param taskDetailDto 任务Id Dto
     * @return 任务详情
     */
    @Override
    public TaskDetailVo getTaskInfo(TaskDetailDto taskDetailDto) {
        LambdaQueryWrapper<Task> selectQuery = Wrappers.lambdaQuery();
        selectQuery.eq(Task::getId, taskDetailDto.getId());
        selectQuery.eq(Task::getDeleteFlag, DeletedFlagEnum.SERVICE_FLAGE_UNDELETED.getCode());
        Task taskOne = Optional.ofNullable(taskMapper.selectOne(selectQuery)).orElseGet(() -> {
            return new Task();
        });
        return TaskConverter.INSTANCE.toTaskDetailVo(taskOne);
    }

    /**
     * 任务表分页请求
     *
     * @param pageQueryTaskDto 分页请求Dto
     * @return 任务表列表
     */
    @Override
    public ListRes<TaskPageListVo> pageQueryTasks(PageQueryTaskDto pageQueryTaskDto) {
        LambdaQueryWrapper<Task> select = Wrappers.lambdaQuery();
        select.eq(Task::getDeleteFlag, DeletedFlagEnum.SERVICE_FLAGE_UNDELETED.getCode());
        select.eq(StringUtils.isNotEmpty(pageQueryTaskDto.getTaskStatus()), Task::getTaskStatus,
            pageQueryTaskDto.getTaskStatus());
        select.eq(StringUtils.isEmpty(pageQueryTaskDto.getTaskStatus()), Task::getTaskStatus,
            TaskOrderAction.TASK_ORDER_WAITING.getAction());
        select.like(StringUtils.isNotEmpty(pageQueryTaskDto.getCustomerName()), Task::getCustomerName,
            pageQueryTaskDto.getCustomerName());
        select.eq(pageQueryTaskDto.getEmployeeId() != null, Task::getEmployeeId, pageQueryTaskDto.getEmployeeId());
        Page<Task> page = new Page<>(pageQueryTaskDto.getCurrent(), pageQueryTaskDto.getSize());
        select.orderByDesc(Task::getAppointmentTime);
        IPage<Task> tasksPage = taskMapper.selectPage(page, select);
        List<Task> tasks = Optional.ofNullable(tasksPage.getRecords()).orElseGet(() -> {
            return Lists.newArrayList();
        });
        return new ListRes<>(TaskConverter.INSTANCE.toTaskPageListVo(tasks), (int) tasksPage.getTotal());
    }
}